package com.example.mybluetooth;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattServer;
import android.bluetooth.BluetoothGattServerCallback;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.AdvertiseCallback;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertiseSettings;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.fragment.app.Fragment;

import java.util.UUID;
import android.os.ParcelUuid;
import android.widget.TextView;

import static com.example.mybluetooth.BluetoothUtils.OpenBluetooth_Request_Code;
import static com.example.mybluetooth.BluetoothUtils.bytesToHexString;
import static com.example.mybluetooth.BluetoothUtils.hexStringToBytes;

public class BleServerPageFragment extends Fragment {
//    几个规定的全局常量：
    private final String NotificationDescriptorUUID = "00002902-0000-1000-8000-00805f9b34fb";

    //服务 UUID
    private final UUID UUID_SERVICE = UUID.fromString("0000fff1-0000-1000-8000-00805f9b34fb");
    private final UUID UUID_SERVICE2 = UUID.fromString("0000fff2-0000-1000-8000-00805f9b34fb");

    //特征 UUID
    private final UUID UUID_CHARACTERISTIC = UUID.fromString("0000ff11-0000-1000-8000-00805f9b34fb");
    private final UUID UUID_CHARACTERISTIC2 = UUID.fromString("0000ff12-0000-1000-8000-00805f9b34fb");

    //描述 UUID
    private final UUID UUID_DESCRIPTOR = UUID.fromString(NotificationDescriptorUUID);

    //广播时间(设置为0则持续广播)
    private int mTime = 0;

    //蓝牙管理类
    private BluetoothManager mBluetoothManager;
    //蓝牙设配器
    private BluetoothAdapter mBluetoothAdapter;
    //GattService
    private BluetoothGattService mGattService;
    //GattCharacteristic
    private BluetoothGattCharacteristic mGattCharacteristic;
    //只读的GattCharacteristic
    private BluetoothGattCharacteristic mGattReadCharacteristic;
    //GattDescriptor
    private BluetoothGattDescriptor mGattDescriptor;

    //BLE广播操作类
    private BluetoothLeAdvertiser mBluetoothLeAdvertiser= null;
    //蓝牙广播回调类
    private MyAdvertiseCallback mAdvertiseCallback;
    //广播设置(必须)
    private AdvertiseSettings mAdvertiseSettings;
    //广播数据(必须，广播启动就会发送)
    private AdvertiseData mAdvertiseData;
    //扫描响应数据(可选，当客户端扫描时才发送)
    private AdvertiseData mScanResponseData;

    //GattServer回调
    private BluetoothGattServerCallback mBluetoothGattServerCallback;
    //GattServer
    private BluetoothGattServer mBluetoothGattServer;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);
        return LayoutInflater.from(getActivity()).inflate(
                R.layout.bleserver_page_fragment,container,false);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        //初始化监听
        initListener();
        //初始化蓝牙
        initBluetooth();
    }
//初始化蓝牙设备管理器
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void initBluetooth() {
        //初始化ble设配器
        mBluetoothManager = (BluetoothManager) getActivity().getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = mBluetoothManager.getAdapter();
        //初始化蓝牙回调包
        mAdvertiseCallback = new MyAdvertiseCallback();
    }

//    初始化监听器
    private void initListener() {
        //设置发送广播按钮监听
        Button startAdvertisingBtn = getActivity().findViewById(R.id.startAdvertisingBtn);
        startAdvertisingBtn.setOnClickListener(new View.OnClickListener() {
            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            @Override
            public void onClick(View v) {
                startAdvertising();
            }
        });
        //设置停止广播按钮监听
        Button stopAdvertisingBtn = getActivity().findViewById(R.id.stopAdvertisingBtn);
        stopAdvertisingBtn.setOnClickListener(new View.OnClickListener() {
            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            @Override
            public void onClick(View v) {
                stopAdvertising();
            }
        });
        //设置添加服务按钮监听
        Button addGattServerBtn = getActivity().findViewById(R.id.addGattServerBtn);
        addGattServerBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                addGattServer();
            }
        });
    }

    /**
     * 发送广播
     */
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void startAdvertising() {
        //判断蓝牙是否开启，如果关闭则请求打开蓝牙
        if (mBluetoothAdapter==null||!mBluetoothAdapter.isEnabled()){
            Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(intent, OpenBluetooth_Request_Code);
            return;
        }
        //设置蓝牙广播名称
        Log.d("rickma","开始广播蓝牙名字");
        mBluetoothAdapter.setName("RickMa马的蓝牙设备");

        //初始化广播设置
        AdvertiseSettings.Builder mAdvertiseSettingsBuilder =  new AdvertiseSettings.Builder();
                //设置广播模式，以控制广播的功率和延迟。 ADVERTISE_MODE_LOW_LATENCY为高功率，低延迟
        mAdvertiseSettingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY);
                //设置蓝牙广播发射功率级别
        mAdvertiseSettingsBuilder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH);
                //广播时限。最多180000毫秒。值为0将禁用时间限制。（不设置则为无限广播时长）
        mAdvertiseSettingsBuilder.setTimeout(mTime);
                //设置广告类型是可连接还是不可连接。
        mAdvertiseSettingsBuilder.setConnectable(true);
        mAdvertiseSettings = mAdvertiseSettingsBuilder.build();

        //设置广播报文
        AdvertiseData.Builder mAdvertiseDataBuilder = new AdvertiseData.Builder();
                //设置广播包中是否包含设备名称。
        mAdvertiseDataBuilder.setIncludeDeviceName(true);
                //设置广播包中是否包含发射功率
        mAdvertiseDataBuilder.setIncludeTxPowerLevel(true);
                //设置UUID
        mAdvertiseDataBuilder.addServiceUuid(new ParcelUuid(UUID_SERVICE));
        mAdvertiseDataBuilder.addServiceUuid(new ParcelUuid(UUID_SERVICE2));
        mAdvertiseData= mAdvertiseDataBuilder.build();

        //设置广播扫描响应报文(可选)
        AdvertiseData.Builder mScanResponseDataBuilder = new  AdvertiseData.Builder();
                //自定义服务数据，将其转化为字节数组传入
        mScanResponseDataBuilder.addServiceData(new ParcelUuid(UUID_SERVICE2),new byte[]{2,3,4});
                //设备厂商自定义数据，将其转化为字节数组传入
        mScanResponseDataBuilder.addManufacturerData(0x06, new byte[]{1,2,3});
        mScanResponseData= mScanResponseDataBuilder.build();

        //获取BLE广播操作对象
        //官网建议获取mBluetoothLeAdvertiser时，先做mBluetoothAdapter.isMultipleAdvertisementSupported判断，
        // 但部分华为手机支持Ble广播却还是返回false,所以最后以mBluetoothLeAdvertiser是否不为空且蓝牙打开为准
        mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
        //蓝牙关闭或者不支持
        if (mBluetoothLeAdvertiser != null && mBluetoothAdapter.isEnabled()){
            Log.d("RickMa","mBluetoothLeAdvertiser != null = "+ mBluetoothLeAdvertiser+ "!= null} " +
                    "mBluetoothAdapter.isMultipleAdvertisementSupported = "+mBluetoothAdapter.isMultipleAdvertisementSupported());
            //开始广播（不附带扫描响应报文）
            //mBluetoothLeAdvertiser.startAdvertising(mAdvertiseSettings, mAdvertiseData, mAdvertiseCallback)
            //开始广播（附带扫描响应报文）
            if(mBluetoothLeAdvertiser!=null)
                mBluetoothLeAdvertiser.startAdvertising(mAdvertiseSettings, mAdvertiseData,mScanResponseData, mAdvertiseCallback);
        }else{
            //前面已经确保在蓝牙开启时才广播，排除蓝牙未开启
            displayInfo("该手机芯片不支持BLE广播");
        }
    }

    /**
     * 停止广播
     */
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void stopAdvertising(){
        if(mBluetoothLeAdvertiser!=null){
            mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
            displayInfo("停止Ble广播");
        }
    }

//自定义叠加换行信息提示，
    private void displayInfo(String msg) {
        TextView displayView = getActivity().findViewById(R.id.displayView);
        displayView.post(new Runnable() {
            @Override
            public void run() {
                displayView.setText(displayView.getText() + "\n " + msg);
            }
        });
    }

    /**
     * 添加Gatt 服务和特征
     * 广播是广播，只有添加Gatt服务和特征后，连接才有服务和特征用于数据交换
     */
    private void addGattServer() {

        //初始化Service
        //创建服务，并初始化服务的UUID和服务类型。
        //BluetoothGattService.SERVICE_TYPE_PRIMARY 为主要服务类型
        mGattService = new BluetoothGattService(UUID_SERVICE, BluetoothGattService.SERVICE_TYPE_PRIMARY);

        /*
        初始化特征(添加读写权限)
        在服务端配置特征时，设置BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE,
        那么onCharacteristicWriteRequest()回调时，不需要GattServer进行response才能进行响应。
        以下使用+号连接的properties，原ketlin代码中是使用or关键字表达的，使用反编译后的java代码看，是用+号表示的，所以直接使用此运算符号,
        查看构造函数源码后，发现该参数的判断方法为&位与运算符，因为该属性在定义的时候就是按照位运算赋值的，所以直接按加法是可以的，
        */
        mGattCharacteristic = new BluetoothGattCharacteristic(UUID_CHARACTERISTIC,
                BluetoothGattCharacteristic.PROPERTY_WRITE +
                BluetoothGattCharacteristic.PROPERTY_NOTIFY+
                BluetoothGattCharacteristic.PROPERTY_READ,
                 (BluetoothGattCharacteristic.PERMISSION_WRITE +
                        BluetoothGattCharacteristic.PERMISSION_READ));
        mGattCharacteristic.setValue( new byte[]{17,2,85} );

        //设置只读的特征 （只写同理）
        mGattReadCharacteristic = new BluetoothGattCharacteristic(UUID_CHARACTERISTIC2,
                BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_READ);

        //初始化描述
        mGattDescriptor = new BluetoothGattDescriptor(UUID_DESCRIPTOR,
                BluetoothGattDescriptor.PERMISSION_READ + BluetoothGattDescriptor.PERMISSION_WRITE);
        mGattDescriptor.setValue(new byte[]{12,33,54,45});

        //Service添加特征值
        mGattService.addCharacteristic(mGattCharacteristic);
        mGattService.addCharacteristic(mGattReadCharacteristic);
        //特征值添加描述
        mGattCharacteristic.addDescriptor(mGattDescriptor);
        //初始化GattServer回调
        mBluetoothGattServerCallback = new MyBluetoothGattServerCallback();

        //添加服务
        if (mBluetoothManager != null)
            mBluetoothGattServer = mBluetoothManager.openGattServer(getActivity(), mBluetoothGattServerCallback);
        mBluetoothGattServer.addService(mGattService);
    }
    
    /**
     * 广播回调callback内部类
     * */
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    class MyAdvertiseCallback extends AdvertiseCallback {
        @Override
        public void onStartSuccess(AdvertiseSettings settingsInEffect) {
            super.onStartSuccess(settingsInEffect);
            Button startAdvertisingBtn = getActivity().findViewById(R.id.startAdvertisingBtn);
            //如果持续广播 则不提醒关闭
            if (mTime != 0){
                //设置关闭提醒
                startAdvertisingBtn.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        displayInfo("Ble广播结束");
                    }
                }, mTime);
            }
            StringBuffer advertiseInfo = new StringBuffer("启动Ble广播成功");
            //连接性
            if (settingsInEffect.isConnectable()){
                advertiseInfo.append(", 可连接");
            }else{
                advertiseInfo.append(", 不可连接");
            }
            //广播时长
            if (settingsInEffect.getTimeout() == 0){
                advertiseInfo.append(", 持续广播");
            }else{
                advertiseInfo.append(", 广播时长 "+settingsInEffect.getTimeout()+ " ms");
            }
            displayInfo(advertiseInfo.toString());
        }

        //具体失败返回码可以到官网查看
        @Override
        public void onStartFailure(int errorCode) {
            super.onStartFailure(errorCode);
            if (errorCode == ADVERTISE_FAILED_DATA_TOO_LARGE){
                displayInfo("启动Ble广播失败 数据报文超出31字节");
            }else{
                displayInfo("启动Ble广播失败 errorCode = $errorCode");
            }
        }
    }

    /**
     *
     *    广播回调内部类结束
     *
     */
    private class MyBluetoothGattServerCallback extends BluetoothGattServerCallback {

//        GattServer回调
        @Override
        public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
            super.onConnectionStateChange(device, status, newState);
            Log.d("RickMa 蓝牙连接状态","Server status = "+status + "newState = "+newState);
            if (status == BluetoothGatt.GATT_SUCCESS){
                //连接成功
                if (newState == BluetoothProfile.STATE_CONNECTED){
                    displayInfo(device.getAddress()+" 连接成功");
                }else if (newState == BluetoothProfile.STATE_DISCONNECTED){
                    displayInfo(device.getAddress()+" 断开连接");
                }
            }else{
                displayInfo("onConnectionStateChange status = "+status+" newState = "+newState);
            }
        }

        @Override
        public void onServiceAdded(int status, BluetoothGattService service) {
            super.onServiceAdded(status, service);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                displayInfo("添加Gatt服务成功 UUUID = "+service.getUuid());
            } else {
                displayInfo("添加Gatt服务失败");
            }
        }

        @Override
        public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
            // 响应客户端
            mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS,
                    offset, characteristic.getValue());
            displayInfo(device.getAddress()+" 请求读取特征值:  UUID = "+characteristic.getUuid()+
                    "读取值 = "+ bytesToHexString(characteristic.getValue()));
        }

        @Override
        public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
            super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
            //刷新该特征值
            characteristic.setValue(value);
            // 响应客户端
            mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
            displayInfo(device.getAddress()+" 请求写入特征值:  UUID = "+characteristic.getUuid()+ "写入值 = "+bytesToHexString(value));

            TextView displayView = getActivity().findViewById(R.id.displayView);
            //模拟数据处理，延迟100ms
            displayView.postDelayed(new Runnable() {
                @Override
                public void run() {
                    //改变特征值
                    characteristic.setValue(hexStringToBytes("abcdef"));
                    //回复客户端,让客户端读取该特征新赋予的值，获取由服务端发送的数据
                    mBluetoothGattServer.notifyCharacteristicChanged(device,characteristic,false);
                }
            },100);

        }

        @Override
        public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {
            super.onDescriptorReadRequest(device, requestId, offset, descriptor);
            // 响应客户端
            mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS,
                    offset,descriptor.getValue());
            displayInfo(device.getAddress()+" 请求读取描述值:  UUID = "+ descriptor.getUuid() +
                    "读取值 = "+ bytesToHexString(descriptor.getValue()));
        }

        //描述写入回调
        @Override
        public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
            super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value);
            //刷新描述值
            descriptor.setValue(value);
            // 响应客户端
            mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS,offset, value);

            displayInfo(device.getAddress()+" 请求写入描述值:  UUID = "+descriptor.getUuid()+ "写入值 = "+bytesToHexString(value));
        }

        @Override
        public void onNotificationSent(BluetoothDevice device, int status) {
            super.onNotificationSent(device, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                displayInfo(device.getAddress()+" 通知发送成功");
            }else{
                displayInfo(device.getAddress()+"通知发送失败 status = "+status);
            }
        }
    }


}
